classdef TestTomlDecode < matlab.unittest.TestCase

  properties (TestParameter)
    validInput = struct( ...
        'comment_fullLine', {{ ...
            sprintf('\n# this is a comment\n') ...
          , struct() ...
          , 'Improper interpretation of a comment' ...
                   }} ...
      , 'bareKey_underscore', {{ ...
            'key_text = "value"' ...
          , struct('key_text', 'value') ...
          , 'Did not accept a bare key with an underscore.' ...
                   }} ...
      , 'bareKey_hyphen', {{ ...
          'key-text = "value"' ...
        , struct('key_text', 'value') ...
        , 'Did not accept a bare key with a dash.' ...
                   }} ...
      , 'bareKey_asciiDigit', {{ ...
          'key123 = "value"' ...
        , struct('key123', 'value') ...
        , 'Did not accept a bare key with digits.' ...
                   }} ...
      , 'bareKey_uppercase', {{ ...
          'KEY = "value"' ...
        , struct('KEY', 'value') ...
        , 'Did not accept a bare key with uppercase ASCII.' ...
                   }} ...
      , 'bareKey_allDigits', {{ ...
          '1234 = "value"' ...
        , struct('f1234', 'value') ...
        , 'Did not accept a bare key with only ASCII digits.' ...
                   }} ...
      , 'quotedKey_dotted', {{ ...
          '"127.0.0.1" = "value"' ...
        , struct('f127_0_0_1', 'value') ...
        , 'Did not handle quoted key with invalid format correctly.' ...
                   }} ...
      , 'quotedKey_spaced', {{ ...
          '"character encoding" = "value"' ...
        , struct('character_encoding', 'value') ...
        , 'Did not handle quoted key with space correctly.' ...
                   }} ...
      , 'quotedKey_single', {{ ...
          '''key2'' = "value"' ...
        , struct('key2', 'value') ...
        , 'Did not handle single-quoted key correctly.' ...
                   }} ...
      , 'quotedKey_nested', {{ ...
          '''quoted "value"'' = "value"' ...
        , struct('quoted_value', 'value') ...
        , 'Did not handle nested quoting in key correctly.' ...
                   }} ...
      , 'quotedKey_empty', {{ ...
          '"" = "value"' ...
        , struct('f', 'value') ...
        , 'Did not handle empty quoted key correctly.' ...
                   }} ...
      , 'dottedKey_basic', {{ ...
          'abc.def.ghi = "value"' ...
        , struct('abc', struct('def', struct('ghi', 'value'))) ...
        , 'Did not handle dotted key correctly.' ...
                   }} ...
      , 'dottedKey_quoted', {{ ...
          'abc."def".ghi = "value"' ...
        , struct('abc', struct('def', struct('ghi', 'value'))) ...
        , 'Did not handle dotted and quoted key correctly.' ...
                   }} ...
      , 'dottedKey_nestedQuoting', {{ ...
          'abc."quoted ''value''".ghi = "value"' ...
        , struct('abc', struct('quoted_value', struct('ghi', 'value'))) ...
        , 'Did not handle nested quoting in dotted key correctly.' ...
                   }} ...
      , 'integer_decimal', {{ ...
          'key = 123' ...
        , struct('key', 123) ...
        , 'Did not parse a decimal integer correctly.' ...
                   }} ...
      , 'integer_negative', {{ ...
          'key = -10' ...
        , struct('key', -10) ...
        , 'Did not parse a negative decimal integer correctly.' ...
                   }} ...
      , 'integer_positive', {{ ...
          'key = +10' ...
        , struct('key', 10) ...
        , 'Did not parse a positive decimal integer correctly.' ...
                   }} ...
      , 'integer_binary', {{ ...
          'key = 0b101' ...
        , struct('key', 5) ...
        , 'Did not parse a binary integer correctly.' ...
                   }} ...
      , 'integer_octal', {{ ...
          'key = 0o1234567' ...
        , struct('key', 342391) ...
        , 'Did not parse an octal integer correctly.' ...
                   }} ...
      , 'integer_hexadecimal', {{ ...
          'key = 0xdecaf' ...
        , struct('key', 912559) ...
        , 'Did not parse a hexadecimal integer correctly.' ...
                   }} ...
      , 'integer_zero', {{ ...
          'key = 0' ...
        , struct('key', 0) ...
        , 'Did not parse integer zero correctly.' ...
                   }} ...
      , 'float_positive', {{ ...
          'key = +1.0' ...
        , struct('key', 1) ...
        , 'Did not parse a positive float correctly.' ...
              }} ...
      , 'float_pi', {{ ...
          'key = 3.1415' ...
        , struct('key', 3.1415) ...
        , 'Did not parse a float correctly.' ...
                     }} ...
      , 'float_negative', {{ ...
          'key = -0.01' ...
        , struct('key', -0.01) ...
        , 'Did not parse a negative float correctly.' ...
                          }} ...
      , 'float_positiveExponent', {{ ...
          'key = 5e+22' ...
        , struct('key', 5e22) ...
        , 'Did not parse an exponentiated float correctly.' ...
                   }} ...
      , 'float_unsignedExponent', {{ ...
          'key = 1e6' ...
        , struct('key', 1e6) ...
        , 'Did not parse an exponentiated float correctly.' ...
                   }} ...
      , 'float_negativeExponent', {{ ...
          'key = -2E-2' ...
        , struct('key', -2e-2) ...
        , 'Did not parse an exponentiated float correctly.' ...
                   }} ...
      , 'float_compound', {{ ...
          'key = 6.626e-34' ...
        , struct('key', 6.626e-34) ...
        , 'Did not parse a float with integral, fractional, and exponential parts correctly.' ...
                          }} ...
      , 'specialFloat_Inf', {{ ...
          'key = inf' ...
        , struct('key', inf) ...
        , 'Did not parse infinity correctly' ...
                   }} ...
      , 'specialFloat_negativeInf', {{ ...
          'key = -inf' ...
        , struct('key', -inf) ...
        , 'Did not parse negative infinity correctly' ...
                   }} ...
      , 'specialFloat_NaN', {{ ...
          'key = nan' ...
        , struct('key', NaN) ...
        , 'Did not parse not-a-number correctly' ...
                   }} ...
      , 'specialFloat_negativeNaN', {{ ...
          'key = -nan' ...
        , struct('key', -NaN) ...
        , 'Did not parse negative not-a-number correctly' ...
                   }} ...
      , 'string_basic', {{ ...
          'key = "value"' ...
        , struct('key', 'value') ...
        , 'Did not parse a basic string successfully.' ...
                   }} ...
      , 'string_literalNewline', {{ ...
          sprintf('key = "line 1\nline 2"') ...
        , struct('key', sprintf('line 1\nline 2')) ...
        , 'Did not parse a basic string with a newline successfully.' ...
                   }} ...
      , 'string_escapedBackspace', {{ ...
          'key = "disappearing A\b"' ...
        , struct('key', sprintf('disappearing A\b')) ...
        , 'Did not parse a basic string with a backspace successfully.' ...
                   }} ...
      , 'string_escapedQuotes', {{ ...
          'key = "escaped \"quote\" marks"' ...
        , struct('key', 'escaped "quote" marks') ...
        , 'Did not parse a basic string with escaped quotes successfully.' ...
                   }} ...
      , 'string_escapedUnicodeShort', {{ ...
          'key = "inline \u0075nicode"' ...
        , struct('key', 'inline unicode') ...
        , 'Did not parse a basic string with short Unicode successfully.' ...
                   }} ...
      , 'string_escapedUnicodeLong', {{ ...
          'key = "inline \U00000055nicode"' ...
        , struct('key', 'inline Unicode') ...
        , 'Did not parse a basic string with long Unicode successfully.' ...
                   }} ...
      , 'string_escapedTab', {{ ...
          'key = "escaped\ttab"' ...
        , struct('key', sprintf('escaped\ttab')) ...
        , 'Did not parse a basic string with an escaped tab successfully.' ...
                   }} ...
      , 'multilineString_basic', {{ ...
          sprintf('key = """\nabcd"""') ...
        , struct('key', 'abcd') ...
        , 'Did not parse a multiline basic string successfully.' ...
                   }} ...
      , 'multilineString_indent', {{ ...
          sprintf('key = """line 1\n    line 2"""') ...
        , struct('key', sprintf('line 1\n    line 2')) ...
        , 'Did not parse a multiline basic string with indentation successfully.' ...
                   }} ...
      , 'multilineString_backslash', {{ ...
          sprintf('key = """on the \\\n    same line"""') ...
        , struct('key', sprintf('on the same line')) ...
        , 'Did not parse a multiline basic string with a LEB successfully.' ...
                   }} ...
      , 'literalString_backslash', {{ ...
          'key = ''C:\Users\example.txt''' ...
        , struct('key', 'C:\Users\example.txt') ...
        , 'Did not parse a literal string with backslashes successfully.' ...
                   }} ...
      , 'literalString_newline', {{ ...
          sprintf('key = ''''''\nNo leading newline here.''''''') ...
        , struct('key', 'No leading newline here.') ...
        , 'Did not parse a literal string with a leading newline successfully.' ...
                   }} ...
      , 'dateTime_fullZ', {{ ...
          'odt = 1979-05-27T07:32:00Z' ...
        , struct('odt', datetime('1979-05-27 07:32:00', 'TimeZone', 'UTC')) ...
        , 'Did not parse a fully qualified datetime successfully.' ...
                   }} ...
      , 'dateTime_fullOffset', {{ ...
          'odt = 1979-05-27T07:32:00-07:00' ...
        , struct('odt', datetime('1979-05-27 07:32:00-07:00', 'InputFormat', ...
                   'yyyy-MM-dd HH:mm:ssZ', 'TimeZone', 'UTC')) ...
        , 'Did not parse a fully qualified datetime successfully.' ...
                   }} ...
      , 'dateTime_fullFracSeconds', {{ ...
          'odt = 1979-05-27T07:32:00.999999-07:00' ...
        , struct('odt', datetime('1979-05-27 07:32:00.999999-07:00', 'InputFormat', ...
                   'yyyy-MM-dd HH:mm:ss.SSSSSSSSSZ', 'TimeZone', 'UTC')) ...
        , 'Did not parse a fully qualified datetime successfully.' ...
                   }} ...
      , 'dateTime_fullNoT', {{ ...
          'odt = 1979-05-27 07:32:00Z'...
        , struct('odt', datetime('1979-05-27 07:32:00', 'TimeZone', 'UTC')) ...
        , 'Did not parse a fully qualified datetime successfully.' ...
                   }} ...
      , 'dateTime_local', {{ ...
          'odt = 1979-05-27T07:32:00' ...
        , struct('odt', datetime('1979-05-27 07:32:00')) ...
        , 'Did not parse a local datetime successfully.' ...
                   }} ...
      , 'dateTime_localFracSec', {{ ...
          'odt = 1979-05-27T07:32:00.999999' ...
        , struct('odt', datetime('1979-05-27 07:32:00.999999', 'InputFormat', ...
                   'yyyy-MM-dd HH:mm:ss.SSSSSSSSS')) ...
        , 'Did not parse a local datetime successfully.' ...
                   }} ...
      , 'dateTime_localNoT', {{ ...
          'odt = 1979-05-27 07:32:00'...
        , struct('odt', datetime('1979-05-27 07:32:00')) ...
        , 'Did not parse a local datetime successfully.' ...
                   }} ...
      , 'date_local', {{ ...
          'odt = 1979-05-27' ...
        , struct('odt', datetime('1979-05-27')) ...
        , 'Did not parse a local date successfully.' ...
                   }} ...
      , 'time_local', {{ ...
          'odt = 07:32:00' ...
        , struct('odt', datetime('07:32:00')) ...
        , 'Did not parse a local time successfully.' ...
                   }} ...
      , 'time_localFracSec', {{ ...
          'odt = 07:32:00.999999' ...
        , struct('odt', datetime('07:32:00.999999', 'InputFormat', ...
                   'HH:mm:ss.SSSSSSSSS')) ...
        , 'Did not parse a local time successfully.' ...
                   }} ...
      , 'array_int', {{ ...
          'key = [1, 2, 3]' ...
        , struct('key', [1, 2, 3]) ...
        , 'Did not parse an array successfully.' ...
                   }} ...
      , 'array_char', {{ ...
          'key = ["a", "b", "c"]' ...
        , struct('key', {{'a', 'b', 'c'}}) ...
        , 'Did not parse an array successfully.' ...
                   }} ...
      , 'array_nested', {{ ...
          'key = [[1, 2], [''a'', "b"]]' ...
        , struct('key', {{[1, 2], {'a', 'b'}}}) ...
        , 'Did not parse an array successfully.' ...
                   }} ...
      , 'array_strings', {{ ...
          'key = ["abcd", "comma, separated, values"]' ...
        , struct('key', {{'abcd', 'comma, separated, values'}}) ...
        , 'Did not parse an array successfully.' ...
                   }} ...
      , 'array_newlines', {{ ...
          sprintf('key = [\n1, 2, 3\n]') ...
        , struct('key', [1, 2, 3]) ...
        , 'Did not parse an array successfully.' ...
                   }} ...
      , 'array_moreNewlines', {{ ...
          sprintf('key = [\n1,\n2,\n]') ...
        , struct('key', [1, 2]) ...
        , 'Did not parse an array successfully.' ...
                   }} ...
      , 'inlineTable_empty', {{ ...
          'tbl = {}' ...
        , struct('tbl', struct()) ...
        , 'Did not parse an inline table successfully.' ...
                   }} ...
      , 'inlineTable_basic', {{ ...
          'tbl = {first = "John", last = "Doe"}' ...
        , struct('tbl', struct('first', 'John', 'last', 'Doe')) ...
        , 'Did not parse an inline table successfully.' ...
                   }} ...
      , 'inlineTable_array', {{ ...
          'tbl = { x = 1, y = ["a", "b"] }' ...
        , struct('tbl', struct('x', 1, 'y', {{'a', 'b'}})) ...
        , 'Did not parse an inline table successfully.' ...
                   }} ...
      , 'inlineTable_nested', {{ ...
          'tbl = { type.name = "cool type" }' ...
        , struct('tbl', struct('type', struct('name', 'cool type'))) ...
        , 'Did not parse an inline table successfully.' ...
                   }} ...
      , 'inlineTable_deepNested', {{ ...
          'tbl = { thing = { wow = "very cool thing" } }' ...
        , struct('tbl', struct('thing', struct('wow', 'very cool thing'))) ...
        , 'Did not parse an inline table successfully.' ...
                   }} ...
      , 'table_basic', {{ ...
          sprintf('[table 1]\nkey1 = "some string"\nkey2 = 123') ...
        , struct('table_1', struct('key1', 'some string', 'key2', 123)) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'table_dotted', {{ ...
          sprintf('[table-1.table-2]\nkey1 = "some string"\nkey2 = 123') ...
        , struct('table_1', struct('table_2', struct('key1', 'some string', ...
                                                     'key2', 123))) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'table_multiple', {{ ...
          sprintf(['[table 1]\nkey1 = "some string"\nkey2 = 123\n\n', ...
                   '[table 2]\nkey1 = "another string"\nkey2 = 456']) ...
        , struct('table_1', struct('key1', 'some string', 'key2', 123), ...
                 'table_2', struct('key1', 'another string', 'key2', 456)) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'table_quotedDotted', {{ ...
          sprintf('[dog."tater.man"]\ntype.name = "pug"') ...
        , struct('dog', struct('tater_man', struct('type', struct('name', 'pug')))) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'table_nestedDotted', {{ ...
          sprintf('[x.y.z.w]') ...
        , struct('x', struct('y', struct('z', struct('w', struct())))) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'table_spacedKey', {{ ...
          sprintf('[ a.b.c ]') ...
        , struct('a', struct('b', struct('c', struct()))) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'table_multiSpacedKey', {{ ...
          sprintf('[ a . b . c ]') ...
        , struct('a', struct('b', struct('c', struct()))) ...
        , 'Did not parse a table successfully.' ...
                   }} ...
      , 'arrayedTable_basic', {{ ...
          sprintf(['[[products]]\nname = "Hammer"\nsku = 738594937\n\n', ...
                   '[[products]]\n\n' ...
                   '[[products]]\nname = "Nail"\nsku = 28475893\ncolor = "gray"']) ...
        , struct('products', {{struct('name', 'Hammer', 'sku', 738594937), ...
                          struct(), struct('name', 'Nail', 'sku', ...
                                           28475893, 'color', 'gray')}}) ...
        , 'Did not parse an array of tables successfully.' ...
                   }} ...
      , 'arrayedTable_nested', {{ ...
          sprintf(['[[fruit]]\nname = "apple"\n\n[fruit.physical]\n', ...
                   'color = "red"\nshape = "round"\n\n[[fruit.variety]]\n', ...
                   'name = "red delicious"\n\n[[fruit.variety]]\n', ...
                   'name = "granny smith"\n\n[[fruit]]\nname = "banana"\n\n', ...
                   '[[fruit.variety]]\nname = "plantain"']) ...
        , struct('fruit', {{ ...
            struct('name', 'apple', 'physical', ...
                   struct('color', 'red', 'shape', 'round'), ...
                   'variety', {{ ...
                       struct('name', 'red delicious') ...
                     , struct('name', 'granny smith') ...
                   }}), ...
            struct('name', 'banana', 'variety', {{ ...
                struct('name', 'plantain') ...
                   }}) ...
                   }}) ...
        , 'Did not parse an array of tables successfully.' ...
                   }} ...
      , 'redefinedKey_CorrectNested', {{ ...
          sprintf('a.b.c = 1\na.d = 2') ...
        , struct('a', struct('b', struct('c', 1), 'd', 2)) ...
        , 'Did not accept a valid nesting definition.' ...
                   }} ...
        );

    invalidInput= struct( ...
        'noValue', {{ ...
            'key = #' ...
          , 'toml:UnspecifiedValue' ...
          , 'Did not fail for unspecified value.' ...
                    }} ...
      , 'emptyBareKey', {{ ...
          sprintf('\nkey = "value"\n= "value2"') ...
        , 'toml:EmptyBareKey' ...
        , 'Did not fail for empty bare key.' ...
                   }} ...
      , 'decimalIntLeadingZeros_1', {{ ...
          'key = 01' ...
        , 'toml:DecIntLeadingZeros' ...
        , 'Did not raise an error for leading zeros on a decimal integer.' ...
                   }} ...
      , 'decimalIntLeadingZeros_2', {{ ...
          'key = 000123' ...
        , 'toml:DecIntLeadingZeros' ...
        , 'Did not raise an error for leading zeros on a decimal integer.' ...
                   }} ...
      , 'upperCaseBoolean_1', {{ ...
          'key = True' ...
        , 'toml:UppercaseBoolean' ...
        , 'Did not raise an error for uppercase Boolean values.' ...
                   }} ...
      , 'upperCaseBoolean_2', {{ ...
          'key = FALSE' ...
        , 'toml:UppercaseBoolean' ...
        , 'Did not raise an error for uppercase Boolean values.' ...
                   }} ...
      , 'nameCollision', {{ ...
          sprintf(['[[fruit]]\nname = "apple"\n\n' ...
                   '[[fruit.variety]]\nname = "red delicious"\n\n' ...
                   '[fruit.variety]\nname = "granny smith"']) ...
        , 'toml:NameCollision' ...
        , 'Did not reject a name collision between an array and a table.' ...
                   }} ...
      , 'heterogeneousArray', {{ ...
          'key = [1, "abc"]' ...
        , 'toml:HeterogeneousArray' ...
        , 'Did not reject a heterogeneous array.' ...
                   }} ...
      , 'redefinedArray', {{ ...
          sprintf('fruit = []\n[[fruit]]') ...
        , 'toml:RedefinedArray' ...
        , 'Did not reject a redefined array.' ...
                   }} ...
      , 'redefinedTable_1', {{ ...
          sprintf('[a]\nb = 1\n\n[a]\nc = 2') ...
        , 'toml:RedefinedTable' ...
        , 'Did not reject a redefined table.' ...
                   }} ...
      , 'redefinedTable_2', {{ ...
          sprintf('[a]\nb = 1\n\n[a.b]\nc = 2') ...
        , 'toml:RedefinedTable' ...
        , 'Did not reject a redefined table.' ...
                   }} ...
      , 'validation_unquotedString', {{ ...
          'key = value' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: identifier used as value.' ...
                   }} ...
      , 'validation_undefinedInteger', {{ ...
          'key = 0q123' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: invalid integer format.' ...
                   }} ...
      , 'validation_rawHTML', {{ ...
          'key = <a href="github.com">good stuff here</a>' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: unquoted HTML.' ...
                   }} ...
      , 'validation_rawJSON', {{ ...
          'key = {"key1": null, "key2": true}' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: unquoted JSON.' ...
                   }} ...
      , 'validation_unquotedCurlyBrace', {{ ...
          'key = }' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: unquoted symbol.' ...
                   }} ...
      , 'validation_unquotedSquareBracket', {{ ...
          'key = ]' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: unquoted symbol.' ...
                   }} ...
      , 'validation_unquotedDot', {{ ...
          'key = .' ...
        , 'toml:InvalidType' ...
        , 'Failed to reject invalid TOML data: unquoted symbol.' ...
                   }} ...
      , 'validation_incompleteArray', {{ ...
          'key = ["abcd", "efgh"' ...
        , 'toml:IncompleteArray' ...
        , 'Failed to reject invalid TOML data: unclosed array.' ...
                   }} ...
      , 'validation_incompleteStringDouble', {{ ...
          'key = "abcd' ...
        , 'toml:IncompleteString' ...
        , 'Failed to reject invalid TOML data: unclosed double quote.' ...
                   }} ...
      , 'validation_incompleteStringSingle', {{ ...
          'key = ''abcd' ...
        , 'toml:IncompleteString' ...
        , 'Failed to reject invalid TOML data: unclosed single quote.' ...
                   }} ...
      , 'validation_incompleteTable', {{ ...
          'key = {subkey = "value"' ...
        , 'toml:IncompleteInlineTable' ...
        , 'Failed to reject invalid TOML data: unclosed inline table.' ...
                   }} ...
      , 'validation_incompleteStringWithTrailingLine', {{ ...
          sprintf('key1 = "abcd\nkey2 = 1234') ...
        , 'toml:IncompleteString' ...
        , ['Failed to reject invalid TOML data: unclosed double quote with ' ...
           'data on the following line.'] ...
                   }} ...
      , 'validation_incompleteMultilineString', {{ ...
          sprintf('key1 = """\nabcd\nefgh\n') ...
        , 'toml:IncompleteString' ...
        , 'Failed to reject invalid TOML data: unclosed multiline string.' ...
                   }} ...
      , 'specialFloat_uppercaseInf', {{ ...
          'key = INF' ...
        , 'toml:UppercaseSpecialFloat' ...
        , 'Did not raise an error for uppercase infinity.' ...
                   }} ...
      , 'specialFloat_uppercaseNaN', {{ ...
          'key = NaN' ...
        , 'toml:UppercaseSpecialFloat' ...
        , 'Did not raise an error for uppercase not-a-number.' ...
                   }} ...
      , 'redefinedKey_basic', {{ ...
          sprintf('name = "Tom"\nname = "Pradyun"') ...
        , 'toml:RedefinedKey' ...
        , 'Did not reject a redefined key.' ...
                   }} ...
      , 'redefinedKey_nested', {{ ...
          sprintf('a.b = 1\na.b.c = 2') ...
        , 'toml:RedefinedKey' ...
        , 'Did not reject a redefined key.' ...
                   }} ...
        );

    invalidEscape = num2cell(setdiff(char(33:126), 'btnfr"\uU'));
  end

  methods (Test, ParameterCombination = 'sequential')

    function testValidInputs(testCase, validInput)
      testCase.verifyEqual(toml.decode(validInput{1}), validInput{2:3})
    end

    function testInvalidInputs(testCase, invalidInput)
      testCase.verifyError(@() toml.decode(invalidInput{1}), invalidInput{2:3})
    end

    function testInvalidStringEscapes(testCase, invalidEscape)
      ch = char(invalidEscape);
      str_to_parse = sprintf('key = "\\%s"', ch);
      testCase.verifyError(@() toml.decode(str_to_parse), ...
                           'toml:InvalidEscapeSequence', ...
                           ['Did not reject a reserved escape sequence: "\', ch, '"'])
    end

  end

end